﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Sharpie.Libs
{
	static class StateManager
	{
		static SpriteBatch _batch;
		static Dictionary<string, Screen> screenList = new Dictionary<string, Screen>();
		static LinkedList<string> stateList = new LinkedList<string>();
		static ChangeType _changeType = ChangeType.None;
		static Texture2D _background;
		static Viewport gameViewport;

		public enum ChangeType
		{
			Forward,
			Back,
			None
		}

		public static Screen ActiveScreen
		{
			get
			{
				return screenList[stateList.Last.Value];
			}
			set
			{
				screenList[stateList.Last.Value] = value;
			}
		}
		public static string ActiveState
		{
			get
			{
				return stateList.Last.Value;
			}
		}


		/// <summary>
		/// Adds a state to list.
		/// </summary>
		/// <param name="id">Name of state added to state list.</param>
		/// <param name="screen"></param>
		static public void AddScreen(string id, Screen screen)
		{
			if (!screenList.ContainsKey(id))
			{
				screenList.Add(id, screen);
			}
		}


		/// <summary>
		/// Replaces current main state with this state.
		/// </summary>
		/// <param name="id">Name of state added to state list.</param>
		static public void SetMainState(string id)
		{
			if (screenList.ContainsKey(id))
			{
				stateList.RemoveFirst();
				stateList.AddFirst(id);
				screenList[stateList.First.Value].Show();
				screenList[stateList.First.Value].SkipAnimation();
			}
		}

		static public void RemoveScreen(string id)
		{
			stateList.Remove(id);
			screenList.Remove(id);
		}

		static public Screen GetScreen(string id)
		{
			return screenList[id];
		}

		static public void ChangeState(string id, Screen.HideStyle hideAnimation)
		{
			if (screenList.ContainsKey(id))
			{
				ActiveScreen.HideAnimation = hideAnimation;
				ActiveScreen.Hide();
				stateList.AddLast(id);
				switch (hideAnimation)
				{
					case Screen.HideStyle.Fade:
						ActiveScreen.HideAnimation = Screen.HideStyle.Fade;
						break;
					case Screen.HideStyle.SlideLeft:
						ActiveScreen.HideAnimation = Screen.HideStyle.SlideRight;
						break;
					case Screen.HideStyle.SlideRight:
						ActiveScreen.HideAnimation = Screen.HideStyle.SlideLeft;
						break;
				}
				ActiveScreen.Show();
				_changeType = ChangeType.Forward;
			}
		}

		/// <summary>
		/// Sets a screen-independent background.
		/// </summary>
		/// <param name="device"></param>
		/// <param name="texture"></param>
		static public void SetBackground(GraphicsDevice device, Texture2D texture)
		{
			_background = texture;
			_batch = new SpriteBatch(device);
			gameViewport = device.Viewport;
		}

		static public void ClearBackground()
		{
			_background = null;
			_batch = null;
		}

		static public void Update(GameTime gameTime)
		{
			if (stateList.Count > 0)
			{
				ActiveScreen.Update(gameTime);
				if (_changeType == ChangeType.Forward)
				{
					if (!screenList[stateList.Last.Previous.Value].IsHidden)
					{
						screenList[stateList.Last.Previous.Value].Update(gameTime);
					}
					else
					{
						_changeType = ChangeType.None;
					}
				}
				else if (_changeType == ChangeType.Back)
				{
					ActiveScreen.Update(gameTime);
					if (!ActiveScreen.IsHidden)
					{
						screenList[stateList.Last.Previous.Value].Update(gameTime);
					}
					else
					{
						stateList.RemoveLast();
						_changeType = ChangeType.None;
					}
				}
			}
		}

		static public void Draw(GameTime gameTime)
		{
			if (_background != null & _batch != null)
			{
				_batch.Begin();
				_batch.Draw(_background, gameViewport.Bounds, Color.White);
				_batch.End();
			}
			if (stateList.Count > 0)
			{
				if (_changeType == ChangeType.Back)
				{
					if (!screenList[stateList.Last.Previous.Value].IsHidden)
					{
						screenList[stateList.Last.Previous.Value].Draw(gameTime);
					}
				}
				ActiveScreen.Draw(gameTime);
				if (_changeType == ChangeType.Forward)
				{
					if (!screenList[stateList.Last.Previous.Value].IsHidden)
					{
						screenList[stateList.Last.Previous.Value].Draw(gameTime);
					}
				}
			}
		}

		static public void PreviousState(Screen.HideStyle hideAnimation)
		{
			if (stateList.Count > 1)
			{
				_changeType = ChangeType.Back;
				ActiveScreen.HideAnimation = hideAnimation;
				ActiveScreen.Hide();
				switch (hideAnimation)
				{
					case Screen.HideStyle.Fade:
						screenList[stateList.Last.Previous.Value].HideAnimation = Screen.HideStyle.Fade;
						break;
					case Screen.HideStyle.SlideLeft:
						screenList[stateList.Last.Previous.Value].HideAnimation = Screen.HideStyle.SlideRight;
						break;
					case Screen.HideStyle.SlideRight:
						screenList[stateList.Last.Previous.Value].HideAnimation = Screen.HideStyle.SlideLeft;
						break;
				}
				screenList[stateList.Last.Previous.Value].Show();
			}
		}

		/// <summary>
		/// Tries to get ID based on screen. If no screen was found, null is returned.
		/// </summary>
		static public string GetId(Screen screen)
		{
			foreach (KeyValuePair<string, Screen> id in screenList)
			{
				if (id.Value == screen) return id.Key;
			}
			return null;
		}
	}
}
